use std::iter::once;
use std::os::windows::ffi::OsStrExt;

use winapi::shared::minwindef::{HKEY, DWORD};
use std::ptr::null_mut;
use winapi::um::winreg::{RegOpenKeyW, RegQueryValueExW, RegSetValueExW, LSTATUS};
use std::ffi::OsStr;
use winapi::shared::winerror::{SEC_E_OK};
use winapi::um::winnt::REG_BINARY;

/// 打开注册表
/// [`main_hkey`] 是一个HKEY值，默认接收[`HKEY_CURRENT_USER`]等值
/// [`sub_key`] HKEY的子健
/// # Examples
/// Basic usage:
/// ```
/// let sub_key = "Software\\360\\333";
/// let hkey_result = reg_util::reg_open(HKEY_CURRENT_USER, sub_key);
/// ```
pub(crate) fn reg_open(main_hkey: HKEY, sub_key: &str) -> Result<HKEY, String> {
    unsafe {
        let mut hkey: HKEY = null_mut();
        let status = RegOpenKeyW(main_hkey,
                                 str_to_lpcwstr(sub_key).as_ptr(),
                                 &mut hkey);
        if status == SEC_E_OK {
            return Result::Ok(hkey);
        }
        return Result::Err(format!("status == {}", status));
    }
}

/// 查询注册表的REG_BINARY的值
pub(crate) fn reg_query_binary(hkey: &HKEY, key_name: &str) -> Vec<u8> {
    unsafe {
        let mut dword: DWORD = 0;
        let mut dtype: DWORD = 0;

        //查询
        let status = RegQueryValueExW(*hkey,
                                      str_to_lpcwstr(key_name).as_ptr(),
                                      null_mut(),
                                      &mut dtype,
                                      null_mut(),
                                      &mut dword);

        let mut data_binary: Vec<u8> = vec![0; dword as usize];
        if status == SEC_E_OK {
            // 存在值

            RegQueryValueExW(*hkey,
                             str_to_lpcwstr(key_name).as_ptr(),
                             null_mut(),
                             &mut dtype,
                             data_binary.as_mut_ptr(),
                             &mut dword);
        }
        return data_binary;
    }
}

///
/// 保存REG_SZ类型的数据
pub(crate) fn reg_save_binary(hkey: &HKEY, key_name: &str, value: &mut Vec<u8>) -> LSTATUS {
    unsafe {
        let status = RegSetValueExW(*hkey,
                                str_to_lpcwstr(key_name).as_ptr(),
                                0,
                                REG_BINARY,
                                value.as_mut_ptr(),
                                value.len() as u32);
        return status;
    }
}

///
/// 字符串字面量转为LPCWSTR
unsafe fn str_to_lpcwstr(str: &str) -> Vec<u16> {
    let result: Vec<u16> = OsStr::new(str).encode_wide().chain(once(0)).collect();
    return result;
}